{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**程序初始模板**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-10T11:31:24.357571Z",
     "start_time": "2019-03-10T11:31:24.086689Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "\n",
    "def main():\n",
    "    pass\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实战 1：切割出小猫\n",
    "![](https://ws1.sinaimg.cn/large/acbcfa39gy1g0xxajfucaj20m80go0ud.jpg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-10T12:09:41.994679Z",
     "start_time": "2019-03-10T12:09:02.088624Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "def sobel_edge(src, ddepth, ksize):\n",
    "    \"\"\"\n",
    "    src: 灰度图像\n",
    "    ddepth:  cv2.CV_64F\n",
    "    ksize: eg. 3、5\n",
    "    \"\"\"\n",
    "    sobelx = cv2.Sobel(src, ddepth, 1, 0, ksize)\n",
    "    sobely = cv2.Sobel(src, ddepth, 0, 1, ksize)\n",
    "    sobelx_abs = cv2.convertScaleAbs(sobelx)\n",
    "    sobely_abs = cv2.convertScaleAbs(sobely)\n",
    "    dst = cv2.addWeighted(sobelx_abs, 0.5, sobely_abs, 0.5, 0)\n",
    "    cv2.imshow(\"sobel_edge\", dst)\n",
    "    return dst\n",
    "\n",
    "\n",
    "def dilation_demo(img):\n",
    "    \"\"\"\n",
    "    膨胀\n",
    "    \"\"\"\n",
    "    # kernel = np.ones((5,5), np.uint8)\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
    "    dilation  = cv2.dilate(img, kernel, iterations = 1)\n",
    "    cv2.imshow(\"dilation\", dilation)\n",
    "    return dilation\n",
    "    \n",
    "def opening_demo(img):\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
    "    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)\n",
    "    #opening = cv2.morphologyEx(img, 2, kernel) # 同上\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "\n",
    "    \n",
    "    \n",
    "def closing_demo(img):\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))\n",
    "    closing = cv2.morphologyEx(img, 3, kernel)\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "    \n",
    "def gradient_demo(img):\n",
    "    \"\"\"\n",
    "    img: 灰度图像\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))\n",
    "    gradient  = cv2.morphologyEx(img, 4, kernel)\n",
    "    \n",
    "    cv2.imshow(\"gradient \", gradient)\n",
    "    return gradient\n",
    "    \n",
    "    \n",
    "def cat_demo(src):\n",
    "    gray = cv2.cvtColor(src, 6)\n",
    "    #dilation = dilation_demo(gray)\n",
    "    #edge = sobel_edge(gray, cv2.CV_64F, 3)\n",
    "    canny = cv2.Canny(gray, 27, 80)\n",
    "    _, thresh = cv2.threshold(canny, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n",
    "    cv2.imshow(\"thresh\", thresh)\n",
    "    dilation = dilation_demo(thresh)\n",
    "    gradient_demo(dilation)\n",
    "    closing_demo(dilation)\n",
    "    \n",
    "\n",
    "def main():\n",
    "    img = cv2.imread(\"./sample_img/cat.jpg\")\n",
    "    #img = img[235:516, 120:685, :]\n",
    "    blur = cv2.GaussianBlur(img, (3, 3), 0) \n",
    "    cv2.imshow(\"src\", img)\n",
    "    cat_demo(blur)\n",
    "\n",
    "\n",
    "\n",
    "main()\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 车牌定位"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 基本知识（场景分析，分析研究对象的常识，基本特征，方便进一步分析）——常识角度、图像处理角度\n",
    "\n",
    "我国的汽车牌照一般由七个字符和一个点组成，车牌字符的高度和宽度是固定的，分别为90mm和45mm，七个字符之间的距离也是固定的12mm，点分割符的直径是10mm，当然字符间的差异可能会引起字符间的距离变化。在民用车牌中，字符的排列位置遵循以下规律：第一个字符通常是我国各省区的简称，用汉字表示；第二个字符通常是发证机关的代码号，最后五个字符由英文字母和数字组合而成，字母是二十四个大写字母（除去I和O这两个字母）的组合，数字用＂０-９＂之间的数字表示。\n",
    "\n",
    "从图像处理角度看，汽车牌照有以下几个特征：\n",
    "- 第一个特征是是车牌的几何特征，即车牌形状统一为长宽高固定的矩形；\n",
    "- 第二个特征是车牌的灰度分布呈现出连续的波谷－波峰－波谷分布，这是因为我国车牌颜色单一，字符直线排列；\n",
    "- 第三个特征是车牌直方图呈现出双峰状的特点，即车牌直方图中可以看到双个波峰；\n",
    "- 第四个特征是车牌具有强边缘信息，这是因为车牌的字符相对集中在车牌的中心，而车牌边缘无字符，因此车牌的边缘信息感较强；\n",
    "- 第五个特征是车牌的字符颜色和车牌背景颜色对比鲜明。目前，我国国内的车牌大致可分为蓝底白字和黄底黑字，特殊用车采用白底黑字或黑底白字，有时辅以红色字体等。为了简化处理，本次学习中只考虑蓝底白字的车牌。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 图像处理过程\n",
    "- 图像大小预处理\n",
    "- 灰度处理\n",
    "- 图像降噪\n",
    "- 形态学处理\n",
    "- 阈值处理，使用轨迹条进行调试\n",
    "- 边缘检测\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://github.com/zeusees/HyperLPR\n",
    "\n",
    "https://blog.csdn.net/relocy?t=1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T03:06:46.056195Z",
     "start_time": "2019-03-25T03:06:43.223860Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "def resize_img(src, MAX_HIGH=1000, rate=1):\n",
    "    \"\"\"\n",
    "    图像大小处理，防止图像太大\n",
    "    img: 输入图像\n",
    "    输出调整后的图像\n",
    "\n",
    "    ———————— 基本原理  ——————————\n",
    "    Opencv 中的 resize 函数\n",
    "    resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst\n",
    "        src: 输入的图像\n",
    "        dsize: 输出图像的大小，设置为 None 则由 fx, fy 决定\n",
    "        fx: 宽度的缩放比例\n",
    "        fy: 高度的缩放比例\n",
    "        interpolation：插值方法\n",
    "        INTER_NEAREST - 最近邻插值 (速度最快)\n",
    "        INTER_LINEAR - 双线性插值（默认） 放大的效果还 OK ，速度较快\n",
    "        INTER_AREA - 使用像素区域关系重新采样。它可能是图像抽取的首选方法，因为它可以提供无莫尔条纹的结果。但是当图像被缩放时，它类似于 INTER_NEAREST 方法。 缩小图像效果最好的方法\n",
    "        INTER_CUBIC - 4x4 像素邻域上的双三次插值 放大的效果最好（慢）\n",
    "        INTER_LANCZOS4 - 8x8 像素邻域的 Lanczos 插值\n",
    "        dst: 输出图像\n",
    "    \"\"\"\n",
    "    img = src\n",
    "    rows, cols = img.shape[0:2]  # 获取图像宽度和高度\n",
    "    if cols > MAX_HIGH:  # 高度超过阈值\n",
    "        change_rate = MAX_HIGH / cols  # 变换的比例\n",
    "        img = cv2.resize(img, (int(rows*change_rate*rate),\n",
    "                               int(cols*change_rate*rate)), interpolation=cv2.INTER_AREA)\n",
    "    return img\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "\n",
    "def trackbar_demo():\n",
    "    \"\"\"\n",
    "    轨迹条函数，不调用，只是写下来，用的时候方便\n",
    "    \"\"\"\n",
    "    cv2.namedWindow(\"dst\")\n",
    "    cv2.createTrackbar(\"low\", \"dst\", 0, 255, nothing)\n",
    "    low = cv2.getTrackbarPos(\"low\", \"dst\")\n",
    "\n",
    "\n",
    "def del_noise(img, ksize=3):\n",
    "    \"\"\"\n",
    "    输入图像和内核大小，内核只能是奇数\n",
    "    输滤波后的图像\n",
    "    —————————基本原理—————————\n",
    "    高斯滤波\n",
    "    GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) -> dst\n",
    "        src: 源图片\n",
    "        ksize: 内核大小,。其中 ksize.width 和 ksize.height 可以不同，但他们都必须为正数和奇数。或者，它们可以是零的，它们都是由 sigma 计算而来。\n",
    "        sigmaX: 表示高斯核函数在 X 方向的的标准偏差。\n",
    "        dst：目标输出\n",
    "        sigmaY：表示高斯核函数在 Y 方向的的标准偏差。若 sigmaY 为零，就将它设为 sigmaX ，如果 sigmaX 和 sigmaY 都是 0，那么就由 ksize.width 和ksize.height 计算出来。\n",
    "        borderType: 边缘处理参数\n",
    "    \"\"\"\n",
    "    blur = cv2.GaussianBlur(img, (ksize, ksize), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    cv2.imshow(\"blur\", blur)\n",
    "    return blur\n",
    "\n",
    "\n",
    "def erosion_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    腐蚀操作:膨胀是取每一个位置邻域内的最小值\n",
    "\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    erosion = cv2.erode(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"erosion\", erosion)\n",
    "    return erosion\n",
    "\n",
    "\n",
    "def dilation_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    膨胀操作:膨胀是取每一个位置邻域内的最大值\n",
    "    —————————基本原理—————————\n",
    "    膨胀后的输出图像的总体亮度的平均值比起原图会有所上升，而图像中较亮物体的尺寸会变大；\n",
    "    相反，较暗物体的尺寸会减小，甚至消失。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    dilation = cv2.dilate(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"dilation\", dilation)\n",
    "    return dilation\n",
    "\n",
    "\n",
    "def opening_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    开运算\n",
    "    先进行 erode 再进行 dilate 的过程就是开运算，\n",
    "    它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)\n",
    "    #opening = cv2.addWeighted(img, 1, opening, -1, 0)\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "    return opening\n",
    "\n",
    "\n",
    "def closing_demo(img, ksize=5):        \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)\n",
    "    return closing\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "\n",
    "def threshold_demo(img, thresh1=127, thresh2=255):\n",
    "    \"\"\"\n",
    "    阈值处理\n",
    "    \"\"\"\n",
    "    _, thresh = cv2.threshold(img, thresh1, thresh1, cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n",
    "    # print(thresh1, thresh2)\n",
    "    cv2.imshow(\"thresh\", thresh)\n",
    "    return thresh\n",
    "\n",
    "def hsv_trace(img):\n",
    "    \"\"\"\n",
    "    颜色跟踪，img 三通道图像\n",
    "    \"\"\"\n",
    "    # BGR -> HSV\n",
    "    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n",
    "\n",
    "    # 在 HSV 中定义蓝色的范围\n",
    "    lower_blue = np.array([100, 43, 46], dtype=np.uint8)\n",
    "    upper_blue = np.array([124, 255, 255], dtype=np.uint8)\n",
    "\n",
    "    # 将 HSV 图像的阈值设置为只获取蓝色\n",
    "    mask = cv2.inRange(hsv, lower_blue, upper_blue) \n",
    "    # 把像素值在 lower_blue 和 upper_blue 之间的像素置 255(白)，之外的置 0（黑）\n",
    "\n",
    "    res = cv2.bitwise_and(img, img, mask=mask) # 白留黑除\n",
    "    cv2.imshow(\"hsv\", hsv) # \n",
    "    cv2.imshow(\"mask\", mask)\n",
    "    cv2.imshow(\"res\", res)\n",
    "\n",
    "def main():\n",
    "\n",
    "\n",
    "    # 把图像读取出来\n",
    "    img = cv2.imread(\"./sample_img/car_num/car_num_4.jpg\")\n",
    "    img = resize_img(src=img)\n",
    "    cv2.imshow(\"src\", img)\n",
    "    \n",
    "    # 灰度化\n",
    "    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "    cv2.imshow(\"gray\", gray)\n",
    "\n",
    "    # 图像滤波，降噪\n",
    "    blur = cv2.GaussianBlur(gray, (3, 3), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    cv2.imshow(\"blur\", blur)\n",
    "    \n",
    "    # 形态学处理，开运算\n",
    "    # 它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    #      对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。 \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 30))\n",
    "    opening = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "    opening = cv2.addWeighted(blur, 1, opening, -1, 0) # 滤波后图像减去开运算后的图像得到\n",
    "    cv2.imshow(\"opening_2\", opening)\n",
    "      \n",
    "    # 寻找边缘\n",
    "    _, thresh = cv2.threshold(opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    canny_img = cv2.Canny(thresh, 10, 200)\n",
    "    cv2.imshow(\"canny_img\", canny_img)\n",
    "    \n",
    "    # --------------------以上的效果不错，重点优化下面---------------------------------------\n",
    "    \n",
    "    \n",
    "    \"\"\"    # 闭运算和开运算之后，使边缘成为一个整体\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 30))\n",
    "    closing = cv2.morphologyEx(canny_img, cv2.MORPH_CLOSE, kernel)\n",
    "    \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))\n",
    "    opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "    cv2.imshow(\"opening_3\", opening)\"\"\"\n",
    "    \n",
    "    \"\"\"\n",
    "    # 查找图像边缘整体形成的矩形区域，可能有很多，车牌就在其中一个\n",
    "    _, contours, hierarchy = cv2.findContours(opening, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    temp_contours = []\n",
    "    Min_Area = 2000\n",
    "    for contour in contours:\n",
    "        if cv2.contourArea(contour) > Min_Area:\n",
    "            temp_contours.append(contour)\n",
    "    \n",
    "    Min_Area = 6000\n",
    "    for contour in contours:\n",
    "        if cv2.contourArea(contour) > Min_Area:\n",
    "            temp_contours.append(contour)\n",
    "\n",
    "    car_plate = []\n",
    "    for temp_contour in temp_contours:\n",
    "        # 返回 ((center_x, center_y), h, w, degree)\n",
    "        rect = cv2.minAreaRect(temp_contour)\n",
    "        rect_h, rect_w = rect[1]\n",
    "        if rect_w > rect_h:  # 保持高永远是最大的\n",
    "            rect_h, rect_w = rect_w, rect_h\n",
    "        rect_rate = rect_h / rect_w\n",
    "        if rect_rate > 2 and rect_rate < 5.5:\n",
    "            car_plate.append(temp_contour)\n",
    "\n",
    "        box = cv2.boxPoints(rect)\n",
    "        box = np.int0(box)\n",
    "        #print(box)\n",
    "        img = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)\n",
    "\n",
    "    cv2.imshow(\"src\", img)\n",
    "    \"\"\"\n",
    "\n",
    "    \n",
    "\n",
    "    \n",
    "    \n",
    "    \n",
    "if __name__ == '__main__':\n",
    "    \n",
    "    main()\n",
    "    cv2.waitKey(0)\n",
    "    cv2.destroyAllWindows()\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[参考](https://blog.csdn.net/weixin_41695564/article/details/79712393)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T02:48:55.311312Z",
     "start_time": "2019-03-25T02:48:52.670831Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "def resize_img(src, MAX_HIGH=1000, rate=1):\n",
    "    \"\"\"\n",
    "    图像大小处理，防止图像太大\n",
    "    img: 输入图像\n",
    "    输出调整后的图像\n",
    "\n",
    "    ———————— 基本原理  ——————————\n",
    "    Opencv 中的 resize 函数\n",
    "    resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst\n",
    "        src: 输入的图像\n",
    "        dsize: 输出图像的大小，设置为 None 则由 fx, fy 决定\n",
    "        fx: 宽度的缩放比例\n",
    "        fy: 高度的缩放比例\n",
    "        interpolation：插值方法\n",
    "        INTER_NEAREST - 最近邻插值 (速度最快)\n",
    "        INTER_LINEAR - 双线性插值（默认） 放大的效果还 OK ，速度较快\n",
    "        INTER_AREA - 使用像素区域关系重新采样。它可能是图像抽取的首选方法，因为它可以提供无莫尔条纹的结果。但是当图像被缩放时，它类似于 INTER_NEAREST 方法。 缩小图像效果最好的方法\n",
    "        INTER_CUBIC - 4x4 像素邻域上的双三次插值 放大的效果最好（慢）\n",
    "        INTER_LANCZOS4 - 8x8 像素邻域的 Lanczos 插值\n",
    "        dst: 输出图像\n",
    "    \"\"\"\n",
    "    img = src\n",
    "    rows, cols = img.shape[0:2]  # 获取图像宽度和高度\n",
    "    if cols > MAX_HIGH:  # 高度超过阈值\n",
    "        change_rate = MAX_HIGH / cols  # 变换的比例\n",
    "        img = cv2.resize(img, (int(rows*change_rate*rate),\n",
    "                               int(cols*change_rate*rate)), interpolation=cv2.INTER_AREA)\n",
    "    return img\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "\n",
    "def trackbar_demo():\n",
    "    \"\"\"\n",
    "    轨迹条函数，不调用，只是写下来，用的时候方便\n",
    "    \"\"\"\n",
    "    cv2.namedWindow(\"dst\")\n",
    "    cv2.createTrackbar(\"low\", \"dst\", 0, 255, nothing)\n",
    "    low = cv2.getTrackbarPos(\"low\", \"dst\")\n",
    "\n",
    "\n",
    "def del_noise(img, ksize=3):\n",
    "    \"\"\"\n",
    "    输入图像和内核大小，内核只能是奇数\n",
    "    输滤波后的图像\n",
    "    —————————基本原理—————————\n",
    "    高斯滤波\n",
    "    GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) -> dst\n",
    "        src: 源图片\n",
    "        ksize: 内核大小,。其中 ksize.width 和 ksize.height 可以不同，但他们都必须为正数和奇数。或者，它们可以是零的，它们都是由 sigma 计算而来。\n",
    "        sigmaX: 表示高斯核函数在 X 方向的的标准偏差。\n",
    "        dst：目标输出\n",
    "        sigmaY：表示高斯核函数在 Y 方向的的标准偏差。若 sigmaY 为零，就将它设为 sigmaX ，如果 sigmaX 和 sigmaY 都是 0，那么就由 ksize.width 和ksize.height 计算出来。\n",
    "        borderType: 边缘处理参数\n",
    "    \"\"\"\n",
    "    blur = cv2.GaussianBlur(img, (ksize, ksize), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    cv2.imshow(\"blur\", blur)\n",
    "    return blur\n",
    "\n",
    "\n",
    "def erosion_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    腐蚀操作:膨胀是取每一个位置邻域内的最小值\n",
    "\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    erosion = cv2.erode(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"erosion\", erosion)\n",
    "    return erosion\n",
    "\n",
    "\n",
    "def dilation_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    膨胀操作:膨胀是取每一个位置邻域内的最大值\n",
    "    —————————基本原理—————————\n",
    "    膨胀后的输出图像的总体亮度的平均值比起原图会有所上升，而图像中较亮物体的尺寸会变大；\n",
    "    相反，较暗物体的尺寸会减小，甚至消失。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    dilation = cv2.dilate(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"dilation\", dilation)\n",
    "    return dilation\n",
    "\n",
    "\n",
    "def opening_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    开运算\n",
    "    先进行 erode 再进行 dilate 的过程就是开运算，\n",
    "    它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)\n",
    "    #opening = cv2.addWeighted(img, 1, opening, -1, 0)\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "    return opening\n",
    "\n",
    "\n",
    "def closing_demo(img, ksize=5):\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)\n",
    "    return closing\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "\n",
    "\n",
    "def threshold_demo(img, thresh1=127, thresh2=255):\n",
    "    \"\"\"\n",
    "    阈值处理\n",
    "    \"\"\"\n",
    "    _, thresh = cv2.threshold(img, thresh1, thresh1,\n",
    "                              cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n",
    "    # print(thresh1, thresh2)\n",
    "    cv2.imshow(\"thresh\", thresh)\n",
    "    return thresh\n",
    "\n",
    "def hsv_trace(img):\n",
    "    \"\"\"\n",
    "    颜色跟踪，img 三通道图像\n",
    "    \"\"\"\n",
    "    # BGR -> HSV\n",
    "    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n",
    "\n",
    "    # 在 HSV 中定义蓝色的范围\n",
    "    lower_blue = np.array([100, 43, 46], dtype=np.uint8)\n",
    "    upper_blue = np.array([124, 255, 255], dtype=np.uint8)\n",
    "\n",
    "    # 将 HSV 图像的阈值设置为只获取蓝色\n",
    "    mask = cv2.inRange(hsv, lower_blue, upper_blue) \n",
    "    # 把像素值在 lower_blue 和 upper_blue 之间的像素置 255(白)，之外的置 0（黑）\n",
    "\n",
    "    res = cv2.bitwise_and(img, img, mask=mask) # 白留黑除\n",
    "    \n",
    "    cv2.imshow(\"res\", res)\n",
    "    return res\n",
    "    #cv2.imshow(\"hsv\", hsv) # \n",
    "    #cv2.imshow(\"mask\", mask)\n",
    "    \n",
    "\n",
    "def main():\n",
    "    # 把图像读取出来\n",
    "    img = cv2.imread(\"./sample_img/car_num/car_num_4.jpg\")\n",
    "    img = resize_img(src=img, rate=0.5)\n",
    "    #cv2.imshow(\"src\", img)\n",
    "    hsv = hsv_trace(img)\n",
    "    # 灰度化\n",
    "    gray = cv2.cvtColor(hsv, cv2.COLOR_BGR2GRAY)\n",
    "    cv2.imshow(\"gray\", gray)\n",
    "\n",
    "    # 图像滤波，降噪\n",
    "    blur = cv2.GaussianBlur(gray, (5, 5), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    cv2.imshow(\"blur\", blur)\n",
    "\n",
    "    # 形态学处理，开运算\n",
    "    # 它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    #      对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n",
    "    opening = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "    opening = cv2.addWeighted(blur, 1, opening, -1, 0)  # 滤波后图像减去开运算后的图像得到\n",
    "    cv2.imshow(\"opening_2\", opening)\n",
    "\n",
    "    # 寻找边缘\n",
    "    _, thresh = cv2.threshold(\n",
    "        opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    canny_img = cv2.Canny(thresh, 100, 200)\n",
    "    cv2.imshow(\"canny_img\", canny_img)\n",
    "\n",
    "    # 闭运算和开运算之后，使边缘成为一个整体\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n",
    "    closing = cv2.morphologyEx(canny_img, cv2.MORPH_CLOSE, kernel)\n",
    "\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n",
    "    opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "    cv2.imshow(\"opening_3\", opening)\n",
    "\n",
    "    # 查找图像边缘整体形成的矩形区域，可能有很多，车牌就在其中一个\n",
    "    _, contours, hierarchy = cv2.findContours(\n",
    "        opening, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    temp_contours = []\n",
    "    Min_Area = 6000\n",
    "    for contour in contours:\n",
    "        if cv2.contourArea(contour) > Min_Area:\n",
    "            temp_contours.append(contour)\n",
    "\n",
    "    car_plate = []\n",
    "    for temp_contour in temp_contours:\n",
    "        # 返回 ((center_x, center_y), h, w, degree)\n",
    "        rect = cv2.minAreaRect(temp_contour)\n",
    "        rect_h, rect_w = rect[1]\n",
    "        if rect_w > rect_h:  # 保持高永远是最大的\n",
    "            rect_h, rect_w = rect_w, rect_h\n",
    "        rect_rate = rect_h / rect_w\n",
    "        if rect_rate > 2 and rect_rate < 5.5:\n",
    "            car_plate.append(temp_contour)\n",
    "\n",
    "        box = cv2.boxPoints(rect)\n",
    "        box = np.int0(box)\n",
    "        #print(box)\n",
    "        img = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)\n",
    "\n",
    "    cv2.imshow(\"src\", img)\n",
    "\n",
    "\n",
    "\n",
    "main()\n",
    "\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 190,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-11T12:46:20.555168Z",
     "start_time": "2019-03-11T12:45:34.471466Z"
    }
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "\n",
    "# 1、读取图像，并把图像转换为灰度图像并显示\n",
    "img = cv2.imread(\"./sample_img/car_num_2.jpg\")  # 读取图片\n",
    "img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)   # 转换了灰度化\n",
    "cv2.imshow('gray', img_gray)  # 显示图片\n",
    "\n",
    "\n",
    "# 2、将灰度图像二值化，设定阈值是100\n",
    "img_thre = img_gray\n",
    "cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY_INV, img_thre)\n",
    "cv2.imshow('threshold', img_thre)\n",
    "\n",
    "\n",
    "# 3、保存黑白图片\n",
    "#cv2.imwrite('thre_res.png', img_thre)\n",
    "\n",
    "# 4、分割字符\n",
    "white = []  # 记录每一列的白色像素总和\n",
    "black = []  # ..........黑色.......\n",
    "height = img_thre.shape[0]\n",
    "width = img_thre.shape[1]\n",
    "white_max = 0\n",
    "black_max = 0\n",
    "# 计算每一列的黑白色像素总和\n",
    "for i in range(width):\n",
    "    s = 0  # 这一列白色总数\n",
    "    t = 0  # 这一列黑色总数\n",
    "    for j in range(height):\n",
    "        if img_thre[j][i] == 255:\n",
    "            s += 1\n",
    "        if img_thre[j][i] == 0:\n",
    "            t += 1\n",
    "    white_max = max(white_max, s)\n",
    "    black_max = max(black_max, t)\n",
    "    white.append(s)\n",
    "    black.append(t)\n",
    "    # print(s)\n",
    "    # print(t)\n",
    "\n",
    "arg = False  # False表示白底黑字；True表示黑底白字\n",
    "if black_max > white_max:\n",
    "    arg = True\n",
    "\n",
    "# 分割图像\n",
    "\n",
    "\n",
    "def find_end(start_):\n",
    "    end_ = start_+1\n",
    "    for m in range(start_+1, width-1):\n",
    "        # 0.95这个参数请多调整，对应下面的0.05\n",
    "        if (black[m] if arg else white[m]) > (0.95 * black_max if arg else 0.95 * white_max):\n",
    "            end_ = m\n",
    "            break\n",
    "    return end_\n",
    "\n",
    "\n",
    "n = 1\n",
    "start = 1\n",
    "end = 2\n",
    "while n < width-2:\n",
    "    n += 1\n",
    "    if (white[n] if arg else black[n]) > (0.05 * white_max if arg else 0.05 * black_max):\n",
    "        # 上面这些判断用来辨别是白底黑字还是黑底白字\n",
    "        # 0.05这个参数请多调整，对应上面的0.95\n",
    "        start = n\n",
    "        end = find_end(start)\n",
    "        n = end\n",
    "        if end-start > 5:\n",
    "            cj = img_thre[1:height, start:end]\n",
    "            cv2.imshow('caijian', cj)\n",
    "            \n",
    "            \n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 效果还可以的 v1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 242,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-11T13:38:58.801865Z",
     "start_time": "2019-03-11T13:35:44.420765Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "\n",
    "\n",
    "def resize_img(src, MAX_HIGH=1000, rate=1):\n",
    "    \"\"\"\n",
    "    图像大小处理，防止图像太大\n",
    "    img: 输入图像\n",
    "    输出调整后的图像\n",
    "\n",
    "    ———————— 基本原理  ——————————\n",
    "    Opencv 中的 resize 函数\n",
    "    resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst\n",
    "        src: 输入的图像\n",
    "        dsize: 输出图像的大小，设置为 None 则由 fx, fy 决定\n",
    "        fx: 宽度的缩放比例\n",
    "        fy: 高度的缩放比例\n",
    "        interpolation：插值方法\n",
    "        INTER_NEAREST - 最近邻插值 (速度最快)\n",
    "        INTER_LINEAR - 双线性插值（默认） 放大的效果还 OK ，速度较快\n",
    "        INTER_AREA - 使用像素区域关系重新采样。它可能是图像抽取的首选方法，因为它可以提供无莫尔条纹的结果。但是当图像被缩放时，它类似于 INTER_NEAREST 方法。 缩小图像效果最好的方法\n",
    "        INTER_CUBIC - 4x4 像素邻域上的双三次插值 放大的效果最好（慢）\n",
    "        INTER_LANCZOS4 - 8x8 像素邻域的 Lanczos 插值\n",
    "        dst: 输出图像\n",
    "    \"\"\"\n",
    "    img = src\n",
    "    rows, cols = img.shape[0:2]  # 获取图像宽度和高度\n",
    "    if cols > MAX_HIGH:  # 高度超过阈值\n",
    "        change_rate = MAX_HIGH / cols  # 变换的比例\n",
    "        img = cv2.resize(img, (int(rows*change_rate*rate),\n",
    "                               int(cols*change_rate*rate)), interpolation=cv2.INTER_AREA)\n",
    "    return img\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "\n",
    "def trackbar_demo():\n",
    "    \"\"\"\n",
    "    轨迹条函数，不调用，只是写下来，用的时候方便\n",
    "    \"\"\"\n",
    "    cv2.namedWindow(\"dst\")\n",
    "    cv2.createTrackbar(\"low\", \"dst\", 0, 255, nothing)\n",
    "    low = cv2.getTrackbarPos(\"low\", \"dst\")\n",
    "\n",
    "\n",
    "def del_noise(img, ksize=3):\n",
    "    \"\"\"\n",
    "    输入图像和内核大小，内核只能是奇数\n",
    "    输滤波后的图像\n",
    "    —————————基本原理—————————\n",
    "    高斯滤波\n",
    "    GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) -> dst\n",
    "        src: 源图片\n",
    "        ksize: 内核大小,。其中 ksize.width 和 ksize.height 可以不同，但他们都必须为正数和奇数。或者，它们可以是零的，它们都是由 sigma 计算而来。\n",
    "        sigmaX: 表示高斯核函数在 X 方向的的标准偏差。\n",
    "        dst：目标输出\n",
    "        sigmaY：表示高斯核函数在 Y 方向的的标准偏差。若 sigmaY 为零，就将它设为 sigmaX ，如果 sigmaX 和 sigmaY 都是 0，那么就由 ksize.width 和ksize.height 计算出来。\n",
    "        borderType: 边缘处理参数\n",
    "    \"\"\"\n",
    "    blur = cv2.GaussianBlur(img, (ksize, ksize), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    cv2.imshow(\"blur\", blur)\n",
    "    return blur\n",
    "\n",
    "\n",
    "def erosion_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    腐蚀操作:膨胀是取每一个位置邻域内的最小值\n",
    "\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    erosion = cv2.erode(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"erosion\", erosion)\n",
    "    return erosion\n",
    "\n",
    "\n",
    "def dilation_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    膨胀操作:膨胀是取每一个位置邻域内的最大值\n",
    "    —————————基本原理—————————\n",
    "    膨胀后的输出图像的总体亮度的平均值比起原图会有所上升，而图像中较亮物体的尺寸会变大；\n",
    "    相反，较暗物体的尺寸会减小，甚至消失。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    dilation = cv2.dilate(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"dilation\", dilation)\n",
    "    return dilation\n",
    "\n",
    "\n",
    "def opening_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    开运算\n",
    "    先进行 erode 再进行 dilate 的过程就是开运算，\n",
    "    它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)\n",
    "    #opening = cv2.addWeighted(img, 1, opening, -1, 0)\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "    return opening\n",
    "\n",
    "\n",
    "def closing_demo(img, ksize=5):        \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)\n",
    "    return closing\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "\n",
    "def threshold_demo(img, thresh1=127, thresh2=255):\n",
    "    \"\"\"\n",
    "    阈值处理\n",
    "    \"\"\"\n",
    "    _, thresh = cv2.threshold(img, thresh1, thresh1, cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n",
    "    # print(thresh1, thresh2)\n",
    "    cv2.imshow(\"thresh\", thresh)\n",
    "    return thresh\n",
    "\n",
    "def hsv_trace(img):\n",
    "    \"\"\"\n",
    "    颜色跟踪，img 三通道图像\n",
    "    \"\"\"\n",
    "    # BGR -> HSV\n",
    "    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n",
    "\n",
    "    # 在 HSV 中定义蓝色的范围\n",
    "    lower_blue = np.array([100, 43, 46], dtype=np.uint8)\n",
    "    upper_blue = np.array([124, 255, 255], dtype=np.uint8)\n",
    "\n",
    "    # 将 HSV 图像的阈值设置为只获取蓝色\n",
    "    mask = cv2.inRange(hsv, lower_blue, upper_blue) \n",
    "    # 把像素值在 lower_blue 和 upper_blue 之间的像素置 255(白)，之外的置 0（黑）\n",
    "\n",
    "    res = cv2.bitwise_and(img, img, mask=mask) # 白留黑除\n",
    "    cv2.imshow(\"hsv\", hsv) # \n",
    "    cv2.imshow(\"mask\", mask)\n",
    "    cv2.imshow(\"res\", res)\n",
    "\n",
    "def main(img):\n",
    "\n",
    "\n",
    "    # 把图像读取出来\n",
    "    #img = cv2.imread(\"./sample_img/car_num/car_num.jpg\")\n",
    "    img = resize_img(src=img, rate=0.5)\n",
    "    #cv2.imshow(\"src\", img)\n",
    "    \n",
    "    # 灰度化\n",
    "    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "    #cv2.imshow(\"gray\", gray)\n",
    "\n",
    "    # 图像滤波，降噪\n",
    "    blur = cv2.GaussianBlur(gray, (3, 3), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    #cv2.imshow(\"blur\", blur)\n",
    "    \n",
    "    # 形态学处理，开运算\n",
    "    # 它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    #      对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。 \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 30))\n",
    "    opening = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)\n",
    "    #cv2.imshow(\"opening\", opening)\n",
    "    opening = cv2.addWeighted(blur, 1, opening, -1, 0) # 滤波后图像减去开运算后的图像得到\n",
    "    #cv2.imshow(\"opening_2\", opening)\n",
    "      \n",
    "    # 寻找边缘\n",
    "    _, thresh = cv2.threshold(opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    canny_img = cv2.Canny(thresh, 10, 200)\n",
    "    #cv2.imshow(\"canny_img\", canny_img)\n",
    "    # --------------------以上的效果不错，重点优化下面---------------------------------------\n",
    "    \n",
    "    \n",
    "     # 闭运算和开运算之后，使边缘成为一个整体\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (26, 26))\n",
    "    closing = cv2.morphologyEx(canny_img, cv2.MORPH_CLOSE, kernel)\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (26, 26))\n",
    "    opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)\n",
    "    #cv2.imshow(\"closing\", closing)\n",
    "    #cv2.imshow(\"opening_3\", opening)\n",
    "    \n",
    "\n",
    "    # 查找图像边缘整体形成的矩形区域，可能有很多，车牌就在其中一个\n",
    "    _, contours, hierarchy = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    temp_contours = []\n",
    "    Min_Area = 15000\n",
    "    for contour in contours:\n",
    "        #area = cv2.contourArea(contour)\n",
    "        #print(area)\n",
    "        if cv2.contourArea(contour) > Min_Area:\n",
    "            temp_contours.append(contour)\n",
    "    \n",
    "\n",
    "    # 根据车牌的物理特征找到它\n",
    "    car_plate = []\n",
    "    for temp_contour in temp_contours:\n",
    "        # 返回 ((center_x, center_y), h, w, degree)\n",
    "        rect = cv2.minAreaRect(temp_contour)\n",
    "        rect_h, rect_w = rect[1]\n",
    "        if rect_w > rect_h:  # 保持高永远是最大的\n",
    "            rect_h, rect_w = rect_w, rect_h\n",
    "        rect_rate = rect_h / rect_w\n",
    "        if rect_rate > 2 and rect_rate < 5.5:\n",
    "            car_plate.append(temp_contour)\n",
    "\n",
    "        box = cv2.boxPoints(rect)\n",
    "        box = np.int0(box)\n",
    "        #print(box)\n",
    "        img = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)\n",
    "    \n",
    "    \n",
    "    cv2.imshow(\"src\"+str(np.random.randint(10)), img)\n",
    "    \n",
    "    \n",
    "\n",
    "    \n",
    "\n",
    "    \n",
    "file = os.listdir(\"./sample_img/car_num/\")\n",
    "\n",
    "for i in range(len(file)):\n",
    "    img = cv2.imread(\"./sample_img/car_num/\"+file[i])    \n",
    "    main(img)\n",
    "\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 效果还可以的 v2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T02:47:14.995900Z",
     "start_time": "2019-03-25T02:47:04.386994Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "\n",
    "\n",
    "def resize_img(src, MAX_HIGH=1000, rate=1):\n",
    "    \"\"\"\n",
    "    图像大小处理，防止图像太大\n",
    "    img: 输入图像\n",
    "    输出调整后的图像\n",
    "\n",
    "    ———————— 基本原理  ——————————\n",
    "    Opencv 中的 resize 函数\n",
    "    resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst\n",
    "        src: 输入的图像\n",
    "        dsize: 输出图像的大小，设置为 None 则由 fx, fy 决定\n",
    "        fx: 宽度的缩放比例\n",
    "        fy: 高度的缩放比例\n",
    "        interpolation：插值方法\n",
    "        INTER_NEAREST - 最近邻插值 (速度最快)\n",
    "        INTER_LINEAR - 双线性插值（默认） 放大的效果还 OK ，速度较快\n",
    "        INTER_AREA - 使用像素区域关系重新采样。它可能是图像抽取的首选方法，因为它可以提供无莫尔条纹的结果。但是当图像被缩放时，它类似于 INTER_NEAREST 方法。 缩小图像效果最好的方法\n",
    "        INTER_CUBIC - 4x4 像素邻域上的双三次插值 放大的效果最好（慢）\n",
    "        INTER_LANCZOS4 - 8x8 像素邻域的 Lanczos 插值\n",
    "        dst: 输出图像\n",
    "    \"\"\"\n",
    "    img = src\n",
    "    rows, cols = img.shape[0:2]  # 获取图像宽度和高度\n",
    "    if cols > MAX_HIGH:  # 高度超过阈值\n",
    "        change_rate = MAX_HIGH / cols  # 变换的比例\n",
    "        img = cv2.resize(img, (int(rows*change_rate*rate),\n",
    "                               int(cols*change_rate*rate)), interpolation=cv2.INTER_AREA)\n",
    "    return img\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    pass\n",
    "\n",
    "\n",
    "def trackbar_demo():\n",
    "    \"\"\"\n",
    "    轨迹条函数，不调用，只是写下来，用的时候方便\n",
    "    \"\"\"\n",
    "    cv2.namedWindow(\"dst\")\n",
    "    cv2.createTrackbar(\"low\", \"dst\", 0, 255, nothing)\n",
    "    low = cv2.getTrackbarPos(\"low\", \"dst\")\n",
    "\n",
    "\n",
    "def del_noise(img, ksize=3):\n",
    "    \"\"\"\n",
    "    输入图像和内核大小，内核只能是奇数\n",
    "    输滤波后的图像\n",
    "    —————————基本原理—————————\n",
    "    高斯滤波\n",
    "    GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) -> dst\n",
    "        src: 源图片\n",
    "        ksize: 内核大小,。其中 ksize.width 和 ksize.height 可以不同，但他们都必须为正数和奇数。或者，它们可以是零的，它们都是由 sigma 计算而来。\n",
    "        sigmaX: 表示高斯核函数在 X 方向的的标准偏差。\n",
    "        dst：目标输出\n",
    "        sigmaY：表示高斯核函数在 Y 方向的的标准偏差。若 sigmaY 为零，就将它设为 sigmaX ，如果 sigmaX 和 sigmaY 都是 0，那么就由 ksize.width 和ksize.height 计算出来。\n",
    "        borderType: 边缘处理参数\n",
    "    \"\"\"\n",
    "    blur = cv2.GaussianBlur(img, (ksize, ksize), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    cv2.imshow(\"blur\", blur)\n",
    "    return blur\n",
    "\n",
    "\n",
    "def erosion_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    腐蚀操作:膨胀是取每一个位置邻域内的最小值\n",
    "\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    erosion = cv2.erode(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"erosion\", erosion)\n",
    "    return erosion\n",
    "\n",
    "\n",
    "def dilation_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    膨胀操作:膨胀是取每一个位置邻域内的最大值\n",
    "    —————————基本原理—————————\n",
    "    膨胀后的输出图像的总体亮度的平均值比起原图会有所上升，而图像中较亮物体的尺寸会变大；\n",
    "    相反，较暗物体的尺寸会减小，甚至消失。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    dilation = cv2.dilate(img, kernel, iterations=1)\n",
    "    cv2.imshow(\"dilation\", dilation)\n",
    "    return dilation\n",
    "\n",
    "\n",
    "def opening_demo(img, ksize=5):\n",
    "    \"\"\"\n",
    "    开运算\n",
    "    先进行 erode 再进行 dilate 的过程就是开运算，\n",
    "    它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。\n",
    "    \"\"\"\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)\n",
    "    #opening = cv2.addWeighted(img, 1, opening, -1, 0)\n",
    "    cv2.imshow(\"opening\", opening)\n",
    "    return opening\n",
    "\n",
    "\n",
    "def closing_demo(img, ksize=5):        \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize, ksize))\n",
    "    closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)\n",
    "    return closing\n",
    "    cv2.imshow(\"closing\", closing)\n",
    "\n",
    "def threshold_demo(img, thresh1=127, thresh2=255):\n",
    "    \"\"\"\n",
    "    阈值处理\n",
    "    \"\"\"\n",
    "    _, thresh = cv2.threshold(img, thresh1, thresh1, cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n",
    "    # print(thresh1, thresh2)\n",
    "    cv2.imshow(\"thresh\", thresh)\n",
    "    return thresh\n",
    "\n",
    "def hsv_trace(img):\n",
    "    \"\"\"\n",
    "    颜色跟踪，img 三通道图像\n",
    "    \"\"\"\n",
    "    # BGR -> HSV\n",
    "    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n",
    "\n",
    "    # 在 HSV 中定义蓝色的范围\n",
    "    lower_blue = np.array([100, 43, 46], dtype=np.uint8)\n",
    "    upper_blue = np.array([124, 255, 255], dtype=np.uint8)\n",
    "\n",
    "    # 将 HSV 图像的阈值设置为只获取蓝色\n",
    "    mask = cv2.inRange(hsv, lower_blue, upper_blue) \n",
    "    # 把像素值在 lower_blue 和 upper_blue 之间的像素置 255(白)，之外的置 0（黑）\n",
    "\n",
    "    res = cv2.bitwise_and(img, img, mask=mask) # 白留黑除\n",
    "    cv2.imshow(\"hsv\", hsv) # \n",
    "    cv2.imshow(\"mask\", mask)\n",
    "    cv2.imshow(\"res\", res)\n",
    "\n",
    "    \n",
    "def main(img):\n",
    "\n",
    "\n",
    "    # 把图像读取出来\n",
    "    #img = cv2.imread(\"./sample_img/car_num/car_num.jpg\")\n",
    "    img = resize_img(src=img, rate=0.5)\n",
    "    #cv2.imshow(\"src\", img)\n",
    "    \n",
    "    # 灰度化\n",
    "    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "    #cv2.imshow(\"gray\", gray)\n",
    "\n",
    "    # 图像滤波，降噪\n",
    "    blur = cv2.GaussianBlur(gray, (3, 3), 0, 0, cv2.BORDER_DEFAULT)\n",
    "    #cv2.imshow(\"blur\", blur)\n",
    "    \n",
    "    # 形态学处理，开运算\n",
    "    # 它具有消除亮度较高的细小区域、在纤细点处分离物体，\n",
    "    #      对于较大物体，可以在不明显改变其面积的情况下平滑其边界等作用。 \n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 30))\n",
    "    opening = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)\n",
    "    #cv2.imshow(\"opening\", opening)\n",
    "    opening = cv2.addWeighted(blur, 1, opening, -1, 0) # 滤波后图像减去开运算后的图像得到\n",
    "    #cv2.imshow(\"opening_2\", opening)\n",
    "      \n",
    "    # 寻找边缘\n",
    "    _, thresh = cv2.threshold(opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    canny_img = cv2.Canny(thresh, 10, 200)\n",
    "    #cv2.imshow(\"canny_img\", canny_img)\n",
    "    # --------------------以上的效果不错，重点优化下面---------------------------------------\n",
    "    \n",
    "    \n",
    "     # 闭运算和开运算之后，使边缘成为一个整体\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (26, 26))\n",
    "    closing = cv2.morphologyEx(canny_img, cv2.MORPH_CLOSE, kernel)\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (26, 26))\n",
    "    opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)\n",
    "    #cv2.imshow(\"closing\", closing)\n",
    "    #cv2.imshow(\"opening_3\", opening)\n",
    "    \n",
    "\n",
    "    # 查找图像边缘整体形成的矩形区域，可能有很多，车牌就在其中一个\n",
    "    _, contours, hierarchy = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    temp_contours = []\n",
    "    Min_Area = 15000\n",
    "    for contour in contours:\n",
    "        #area = cv2.contourArea(contour)\n",
    "        #print(area)\n",
    "        if cv2.contourArea(contour) > Min_Area:\n",
    "            temp_contours.append(contour)\n",
    "    \n",
    "\n",
    "    # 根据车牌的物理特征找到它\n",
    "    car_plate = []\n",
    "    for temp_contour in temp_contours:\n",
    "        # 返回 ((center_x, center_y), h, w, degree)\n",
    "        rect = cv2.minAreaRect(temp_contour)\n",
    "        rect_h, rect_w = rect[1]\n",
    "        if rect_w > rect_h:  # 保持高永远是最大的\n",
    "            rect_h, rect_w = rect_w, rect_h\n",
    "        rect_rate = rect_h / rect_w\n",
    "        if rect_rate > 2 and rect_rate < 5.5:\n",
    "            car_plate.append(temp_contour)\n",
    "\n",
    "        box = cv2.boxPoints(rect)\n",
    "        box = np.int0(box)\n",
    "        #print(box)\n",
    "        img = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)\n",
    "    \n",
    "    \n",
    "    cv2.imshow(\"src\"+str(np.random.randint(10)), img)\n",
    "    \n",
    "    \n",
    "\n",
    "    \n",
    "\n",
    "    \n",
    "file = os.listdir(\"./sample_img/car_num/\")\n",
    "\n",
    "for i in range(len(file)):\n",
    "    img = cv2.imread(\"./sample_img/car_num/\"+file[i])    \n",
    "    main(img)\n",
    "\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# -*- coding: utf-8 -*-\n",
    "# @Author: hyzhangyong\n",
    "# @Date:   2016-06-23 16:21:54\n",
    "# @Last Modified by:   hyzhangyong\n",
    "# @Last Modified time: 2016-06-24 00:00:47\n",
    "import sys\n",
    "import cv2\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "\n",
    "# 旋转图片\n",
    "def rotate(img,path):\n",
    "        im1 = Image.open(img)\n",
    "        im2 = im1.rotate(180)\n",
    "        im2.save(path)\n",
    "\n",
    "def preprocess(gray):\n",
    "        # # 直方图均衡化\n",
    "        equ = cv2.equalizeHist(gray)\n",
    "        # 高斯平滑\n",
    "        gaussian = cv2.GaussianBlur(gray, (3, 3), 0, 0, cv2.BORDER_DEFAULT)\n",
    "        # 中值滤波\n",
    "        median = cv2.medianBlur(gaussian, 5)\n",
    "        # Sobel算子，X方向求梯度\n",
    "        sobel = cv2.Sobel(median, cv2.CV_8U, 1, 0, ksize = 3)\n",
    "        # 二值化\n",
    "        ret, binary = cv2.threshold(sobel, 170, 255, cv2.THRESH_BINARY)\n",
    "        # 膨胀和腐蚀操作的核函数\n",
    "        element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))\n",
    "        element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 7))\n",
    "        # 膨胀一次，让轮廓突出\n",
    "        dilation = cv2.dilate(binary, element2, iterations = 1)\n",
    "        # 腐蚀一次，去掉细节\n",
    "        erosion = cv2.erode(dilation, element1, iterations = 1)\n",
    "        # 再次膨胀，让轮廓明显一些\n",
    "        dilation2 = cv2.dilate(erosion, element2,iterations = 3)\n",
    "        # cv2.imshow('dilation2',dilation2)\n",
    "        # cv2.waitKey(0)\n",
    "        return dilation2\n",
    "\n",
    "def findPlateNumberRegion(img):\n",
    "        region = []\n",
    "        # 查找轮廓\n",
    "        contours,hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "        # 筛选面积小的\n",
    "        for i in range(len(contours)):\n",
    "                cnt = contours[i]\n",
    "                # 计算该轮廓的面积\n",
    "                area = cv2.contourArea(cnt)\n",
    "\n",
    "                # 面积小的都筛选掉\n",
    "                if (area < 2000):\n",
    "                        continue\n",
    "\n",
    "                # 轮廓近似，作用很小\n",
    "                epsilon = 0.001 * cv2.arcLength(cnt,True)\n",
    "                approx = cv2.approxPolyDP(cnt, epsilon, True)\n",
    "\n",
    "                # 找到最小的矩形，该矩形可能有方向\n",
    "                rect = cv2.minAreaRect(cnt)\n",
    "                print \"rect is: \"\n",
    "                print rect\n",
    "\n",
    "                # box是四个点的坐标\n",
    "                box = cv2.cv.BoxPoints(rect)\n",
    "                box = np.int0(box)\n",
    "\n",
    "                # 计算高和宽\n",
    "                height = abs(box[0][1] - box[2][1])\n",
    "                width = abs(box[0][0] - box[2][0])\n",
    "                # 车牌正常情况下长高比在2.7-5之间\n",
    "                ratio =float(width) / float(height)\n",
    "                print ratio\n",
    "                if (ratio > 5 or ratio < 2):\n",
    "                        continue\n",
    "                region.append(box)\n",
    "\n",
    "        return region\n",
    "\n",
    "def detect(img):\n",
    "        # 转化成灰度图\n",
    "        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "        \n",
    "        # 形态学变换的预处理\n",
    "        dilation = preprocess(gray)\n",
    "        # 查找车牌区域\n",
    "        region = findPlateNumberRegion(dilation)\n",
    "\n",
    "        # 用绿线画出这些找到的轮廓\n",
    "        for box in region:\n",
    "                cv2.drawContours(img, [box], 0, (0, 255, 0), 2)\n",
    "        ys = [box[0, 1], box[1, 1], box[2, 1], box[3, 1]]\n",
    "        xs = [box[0, 0], box[1, 0], box[2, 0], box[3, 0]]\n",
    "        ys_sorted_index = np.argsort(ys)\n",
    "        xs_sorted_index = np.argsort(xs)\n",
    "\n",
    "        x1 = box[xs_sorted_index[0], 0]\n",
    "        x2 = box[xs_sorted_index[3], 0]\n",
    "\n",
    "        y1 = box[ys_sorted_index[0], 1]\n",
    "        y2 = box[ys_sorted_index[3], 1]\n",
    "\n",
    "        img_org2 = img.copy()\n",
    "        img_plate = img_org2[y1:y2, x1:x2]\n",
    "\n",
    "        return img_plate\n",
    "        # cv2.imshow('number plate', img_plate)\n",
    "        # cv2.imwrite('number_plate'+str(i)+'.jpg', img_plate)\n",
    "\n",
    "        # cv2.namedWindow('img', cv2.WINDOW_NORMAL)\n",
    "        # cv2.imshow('img', img)\n",
    "\n",
    "        # 带轮廓的图片\n",
    "        # cv2.imwrite('contours.png', img)\n",
    "\n",
    "        # cv2.waitKey(0)\n",
    "        # 关闭窗口\n",
    "        # cv2.destroyAllWindows()\n",
    "def mul(img):\n",
    "        a,b,c=img.shape\n",
    "        return a*b\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "        for i in range(1,11):\n",
    "                image1 = \"image/\"+str(i)+\".jpg\"\n",
    "                rotate(image1,\"temp/temp.jpg\")\n",
    "                image2= \"temp/temp.jpg\"\n",
    "\n",
    "                img = cv2.imread(image1)\n",
    "                image11=detect(img)\n",
    "                # 旋转后的\n",
    "                img = cv2.imread(image2)\n",
    "                image12=detect(img)\n",
    "                a=mul(image11)\n",
    "                b=mul(image12)\n",
    "                if a>b:\n",
    "                        cv2.imwrite('number_plate'+str(i)+'.jpg', image11)\n",
    "                else:\n",
    "                        cv2.imwrite('number_plate'+str(i)+'.jpg', image12)\n",
    "                        rotate('number_plate'+str(i)+'.jpg','number_plate'+str(i)+'.jpg')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T07:18:03.773595Z",
     "start_time": "2019-03-25T07:17:29.514243Z"
    }
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T07:27:31.842906Z",
     "start_time": "2019-03-25T07:27:31.839910Z"
    }
   },
   "outputs": [],
   "source": [
    "import hyperlpr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T07:27:32.226687Z",
     "start_time": "2019-03-25T07:27:32.222691Z"
    }
   },
   "outputs": [],
   "source": [
    "import cv2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T07:27:32.645446Z",
     "start_time": "2019-03-25T07:27:32.579483Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[148, 176, 427, 249]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img = cv2.imread(\"./sample_img/car_num/car_num.jpg\")\n",
    "local = hyperlpr.HyperLPR_PlateRecogntion(img)[0][2]\n",
    "local"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-25T07:40:44.007225Z",
     "start_time": "2019-03-25T07:40:37.258061Z"
    }
   },
   "outputs": [],
   "source": [
    "cv2.imshow(\"img\", img)\n",
    "\n",
    "\n",
    "#cv2.rectangle(img, (10, 20), (10, 30), (0, 255, 0))\n",
    "cv2.rectangle(img, (local[0], local[1]), (local[2], local[3]), (0, 255, 0), 5)\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
